﻿#if UNITY_EDITOR
using UnityEditor;
using UnityEditorInternal;
#endif
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml.Serialization;

namespace Hont.BlazingNodePackage
{
    public sealed class NodeGroup : Node, ICustomGUINode
    {
#if UNITY_EDITOR
        ReorderableList mInReorderableList;
        ReorderableList mOutReorderableList;
#endif

        public string seriName;

        public override string Category { get { return "System"; } }
        public override string Name { get { return seriName; } }

        [XmlIgnore]
        public override IParameter LinkedParameter { get { return null; } set { } }

        public override object Context
        {
            get
            {
                return base.Context;
            }

            set
            {
                if (SubGraph == null) return;

                for (int i = 0; i < SubGraph.NodeList.Count; i++)
                {
                    var item = SubGraph.NodeList[i];
                    item.Context = value;
                }

                base.Context = value;
            }
        }


        public NodeGroup()
            : this("Group", false)
        {
        }

        public NodeGroup(string name)
            : this(name, true)
        {
        }

        public NodeGroup(string name, bool initNode)
        {
            seriName = name;

            if (initNode)
                Init();
        }

        public void SyncSubNodes(NodeGroupIn groupIn, NodeGroupOut groupOut)
        {
            if (groupIn.OutPortList.Count != InPortList.Count)
            {
                groupIn.OutPortList.Clear();

                for (int i = 0; i < InPortList.Count; i++)
                {
                    groupIn.OutPortList.Add(new Port() { Host = groupIn, Name = InPortList[i].Name, Value = InPortList[i].Value });
                }

                if (groupIn.OutPortList.Count > 0)
                    groupIn.OutPortList[0].IsSpark = true;
            }

            if (groupOut != null && groupOut.InPortList.Count != OutPortList.Count)
            {
                groupOut.InPortList.Clear();

                for (int i = 0; i < OutPortList.Count; i++)
                {
                    groupOut.InPortList.Add(new Port() { Host = groupOut, Name = OutPortList[i].Name });
                }

                if (groupOut.InPortList.Count > 0)
                    groupOut.InPortList[0].IsSpark = true;
            }

            for (int i = 0; i < InPortList.Count; i++)
                groupIn.OutPortList[i].Value = InPortList[i].Value;

            if (groupOut != null)
            {
                for (int i = 0; i < OutPortList.Count; i++)
                    OutPortList[i].Value = groupOut.InPortList[i].ConnectPort == null ? null : groupOut.InPortList[i].ConnectPort.Value;
            }
        }

        protected override void Init()
        {
            base.Init();

        }

        public override void OnAwake(Document document)
        {
            if (SubGraph == null) return;

            for (int i = 0; i < SubGraph.NodeList.Count; i++)
            {
                var item = SubGraph.NodeList[i];

                item.OnAwake(document);
            }
        }

        public override IEnumerator Execute(Port port)
        {
            if (SubGraph == null)
                throw new NullReferenceException("SubGraph is null!");

            var nodeGroupIn = SubGraph.InNode as NodeGroupIn;
            var nodeGroupOut = SubGraph.OutNode as NodeGroupOut;

            if (nodeGroupOut != null)
            {
                nodeGroupOut.OnExecuteFunc = () =>
                {
                    SyncSubNodes(nodeGroupIn, nodeGroupOut);
                    return base.Execute(port);
                };
            }

            SyncSubNodes(nodeGroupIn, nodeGroupOut);

            yield return nodeGroupIn.Execute(port);
        }

        public override void OnDeserialize()
        {
            if (SubGraph == null) return;

            for (int i = 0; i < SubGraph.NodeList.Count; i++)
            {
                var item = SubGraph.NodeList[i];

                item.OnDeserialize();
            }
        }

        public override void OnDestroy()
        {
            if (SubGraph == null) return;

            for (int i = 0; i < SubGraph.NodeList.Count; i++)
            {
                var item = SubGraph.NodeList[i];

                item.OnDestroy();
            }
        }

        public override object Clone()
        {
            if (SubGraph == null) return base.Clone();

            var newGraph = new Graph();

            foreach (var item in SubGraph.NodeList)
                newGraph.NodeList.Add(item.Clone() as INode);

            newGraph.InNode = newGraph.NodeList.Find(m => m is NodeGroupIn);
            newGraph.OutNode = newGraph.NodeList.Find(m => m is NodeGroupOut);

            var cloned = base.Clone() as NodeGroup;
            cloned.SubGraph = newGraph;
            cloned.SubGraph.ParentNode = cloned;

            return cloned;
        }

        public override void OnEditorUpdate()
        {
            if (SubGraph != null)
            {
                SyncSubNodes(SubGraph.InNode as NodeGroupIn, SubGraph.OutNode as NodeGroupOut);
            }
        }

        public override void OnDrawAttributesGUI(out bool updateUI)
        {
            updateUI = false;

#if UNITY_EDITOR
            EditorGUI.BeginChangeCheck();
            seriName = EditorGUILayout.TextField(seriName);
            if (EditorGUI.EndChangeCheck())
                updateUI = true;

            Action<string, ReorderableList> initReorderableList = (header, list) =>
             {
                 var tempHeader = header;
                 var tempList = list;

                 tempList.drawHeaderCallback =
                     (Rect rect) =>
                     {
                         EditorGUI.LabelField(rect, tempHeader);
                     };

                 tempList.drawElementCallback =
                     (Rect rect, int index, bool isActive, bool isFocused) =>
                     {
                         var port = tempList.list[index] as Port;
                         var tempRect = new Rect(rect.x, rect.y, rect.width, rect.height - 2);
                         port.Name = EditorGUI.TextField(tempRect, port.Name.ToString());
                     };

                 tempList.onAddCallback = (sender) =>
                 {
                     sender.list.Add(new Port() { Host = this, Name = "New Port" });
                 };
             };

            if (mInReorderableList == null)
            {
                mInReorderableList = new ReorderableList(mInPortList, typeof(string));
                initReorderableList("In Ports", mInReorderableList);
            }

            if (mOutReorderableList == null)
            {
                mOutReorderableList = new ReorderableList(mOutPortList, typeof(string));
                initReorderableList("Out Ports", mOutReorderableList);
            }

            EditorGUI.BeginChangeCheck();

            mInReorderableList.DoLayoutList();
            mOutReorderableList.DoLayoutList();

            if (EditorGUI.EndChangeCheck())
            {
                updateUI = true;
            }
#endif
        }

        #region --- ICustomGUINode Members ---

        void ICustomGUINode.DrawNode(INode node, bool isSelection, Color color)
        {
#if UNITY_EDITOR

            var oldColor = GUI.color;

            GUI.color = color;

            var rect = new Rect(0, 0, 170, 80);
            rect.center = node.Position;
            GUI.Box(rect, "");

            var nameSize = GUI.skin.label.CalcSize(new GUIContent(seriName));
            var textRect = new Rect(rect.x + rect.width * 0.5f, rect.y + rect.height * 0.5f, nameSize.x, nameSize.y);
            textRect.center -= nameSize * 0.5f;
            GUI.Box(textRect, seriName, GUIStyle.none);

            GUI.color = oldColor;
#endif
        }

        void ICustomGUINode.DrawPort(Rect nodeRect, INode node, int index, bool isInPort)
        {
#if UNITY_EDITOR
            var x = isInPort ? nodeRect.xMin : nodeRect.xMax - 15;
            var y = nodeRect.yMin + 15 + index * 20;
            var portName = isInPort ? node.InPortList[index].Name : node.OutPortList[index].Name;

            GUI.Box(new Rect(x, y, 15, 15), "");
            if (isInPort)
                GUI.Label(new Rect(x + 17, y, 40, 15), portName);
            else
                GUI.Label(new Rect(x - 24, y, 40, 15), portName);

#endif
        }

        Rect ICustomGUINode.GetNodeRect(INode node)
        {
#if UNITY_EDITOR
            var rect = new Rect(0, 0, 170, 80);
            rect.center = node.Position;

            return rect;
#else
            return new Rect();
#endif
        }

        Rect ICustomGUINode.GetPortRect(Rect nodeRect, INode node, int index, bool isInPort)
        {
#if UNITY_EDITOR
            var x = isInPort ? nodeRect.xMin : nodeRect.xMax - 15;
            var y = nodeRect.yMin + 15 + index * 20;

            return new Rect(x, y, 15, 15);
#else
            return new Rect();
#endif
        }

        #endregion
    }
}
